﻿// ------------------------------------------------------------------------------
// <copyright file="MeridiemScroller.cs" company="skumaronline.com">
//    Copyright (C) 2012 Suresh Kumar T.
//
//    This program is free software; you can redistribute it and/or
//    modify it under the terms of the GNU General Public License
//    as published by the Free Software Foundation version 2
//    of the License.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    http://www.gnu.org/licenses/gpl-2.0.html
//
// </copyright>
// ------------------------------------------------------------------------------

namespace StoreAppLib.Controls
{
    using System;
    using System.Collections.ObjectModel;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Media.Animation;

    /// <summary>
    /// Meridiem scroll control
    /// </summary>
    public sealed class MeridiemScroller : Control
    {
        /// <summary>
        /// Corner radius property
        /// </summary>
        public static readonly DependencyProperty CornerRadiusProperty =
            DependencyProperty.Register("CornerRadius", typeof(double), typeof(MeridiemScroller), new PropertyMetadata(0));

        /// <summary>
        /// Scroll viewer vertical offset property 
        /// </summary>
        public static readonly DependencyProperty MeridiemVerticalOffSetProperty =
            DependencyProperty.Register("MeridiemVerticalOffSet", typeof(double), typeof(MeridiemScroller), new PropertyMetadata(0.0, OnMeridiemVerticalOffSetPropertyChanged));

        /// <summary>
        /// The scroll viewer
        /// </summary>
        private ScrollViewer scroller;

        /// <summary>
        /// Whether scroll viewer is scrolling
        /// </summary>
        private bool isScrolling = false;

        /// <summary>
        /// Initializes a new instance of the <see cref="MeridiemScroller" /> class
        /// </summary>
        public MeridiemScroller()
        {
            this.DefaultStyleKey = typeof(MeridiemScroller);
        }

        /// <summary>
        /// Value changed event handler
        /// </summary>
        public event ValueChangedEventHandler ValueChanged;

        /// <summary>
        /// Gets or sets value
        /// </summary>
        public string Value { get; set; }

        /// <summary>
        /// Gets or sets corner radius
        /// </summary>
        public double CornerRadius
        {
            get { return (double)this.GetValue(CornerRadiusProperty); }
            set { this.SetValue(CornerRadiusProperty, value); }
        }

        /// <summary>
        /// Gets or sets Scroll viewer vertical offset
        /// </summary>
        internal double MeridiemVerticalOffSet
        {
            get { return (double)this.GetValue(MeridiemVerticalOffSetProperty); }
            set { this.SetValue(MeridiemVerticalOffSetProperty, value); }
        }

        /// <summary>
        /// Occurs when Time Vertical Offset property is set
        /// </summary>
        /// <param name="sender">The source</param>
        /// <param name="args">The property changed event argument</param>
        internal static void OnMeridiemVerticalOffSetPropertyChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            if (args.NewValue != args.OldValue)
            {
                MeridiemScroller thisInstance = sender as MeridiemScroller;
                thisInstance.ScrollToVerticalOffset((double)args.NewValue);
            }
        }

        /// <summary>
        /// Scroll the scroll viewer to specified offset
        /// </summary>
        /// <param name="scrollOffSet">The scroll offset</param>
        internal void ScrollToVerticalOffset(double scrollOffSet)
        {
            if (this.scroller != null)
            {
                this.scroller.ScrollToVerticalOffset(scrollOffSet);
            }
        }

        /// <summary>
        /// Scroll programmatically
        /// </summary>
        /// <param name="down">Up or down</param>
        internal void Scroll(bool down)
        {
            lock (this)
            {
                if ((this.Value == "AM" && down) || (this.Value == "PM" && !down))
                {
                    this.ScrollToVerticalOffset(this.scroller.VerticalOffset + (down ? 50.0 : -50.0));

                    this.OnMeridiemViewChanged(this.scroller, new ScrollViewerViewChangedEventArgs());
                }
            }
        }

        /// <summary>
        ///  Invoked whenever application code or internal processes (such as a rebuilding
        ///  layout pass) call ApplyTemplate. In simplest terms, this means the method
        ///  is called just before a UI element displays in your app. 
        /// </summary>
        protected override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this.scroller = this.GetTemplateChild("scroller") as ScrollViewer;

            this.scroller.ViewChanged += this.OnMeridiemViewChanged;

            this.scroller.Loaded += this.OnScrollViewerLoaded;
        }

        /// <summary>
        /// Scroll to selected value on scroll viewer loaded
        /// </summary>
        /// <param name="sender">The sender</param>
        /// <param name="e">The argument</param>
        private void OnScrollViewerLoaded(object sender, RoutedEventArgs e)
        {
            if (this.Value == "AM")
            {
                this.MeridiemVerticalOffSet = 0;
            }
            else
            {
                this.MeridiemVerticalOffSet = 50;
            }
        }

        /// <summary>
        /// Occurs when scroll viewer is scrolled
        /// </summary>
        /// <param name="sender">The scroll viewer</param>
        /// <param name="e">Scroll change event argument</param>
        private void OnMeridiemViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
        {
            if (!e.IsIntermediate)
            {
                ScrollViewer scrollViewer = sender as ScrollViewer;
                double offSet = scrollViewer.VerticalOffset % 50.0;

                if (offSet == 0)
                {
                    int currentIndex = (int)Math.Round((scrollViewer.VerticalOffset / 50.0) + 1);

                    this.Value = currentIndex == 1 ? "AM" : "PM";

                    if (this.ValueChanged != null)
                    {
                        this.ValueChanged(this, this.Value);
                    }
                }

                if (offSet != 0 && !this.isScrolling)
                {
                    this.isScrolling = true;
                    this.RenderTransform = new CompositeTransform();

                    Storyboard storyboard = new Storyboard();

                    DoubleAnimation animation = new DoubleAnimation();
                    animation.EnableDependentAnimation = true;
                    animation.Duration = TimeSpan.FromSeconds(0.25);
                    animation.From = scrollViewer.VerticalOffset;
                    if (offSet > 25)
                    {
                        animation.To = Math.Round(scrollViewer.VerticalOffset + 50 - offSet, 0);
                    }
                    else
                    {
                        animation.To = Math.Round(scrollViewer.VerticalOffset - offSet, 0);
                    }

                    storyboard.Children.Add(animation);

                    Storyboard.SetTarget(animation, this);
                    Storyboard.SetTargetProperty(animation, "(MeridiemScroller.MeridiemVerticalOffSet)");

                    storyboard.Begin();
                }
            }
            else
            {
                this.isScrolling = false;
            }
        }
    }
}
